﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace LowLevelGraphics
{
    public delegate double testdel(double i);

    /// <summary>
    /// 
    /// </summary>
    public class VectorMatrix
    {
        protected double[,] m_aValue = null;
        protected bool m_bIsIdentity = false;
        protected bool m_bIsSquare = false;
        protected int m_nColumns = -1; 
        protected int m_nRows = -1;

        /// <summary>
        /// Initializes a new instance of the <see cref="VectorMatrix"/> class.
        /// </summary>
        public VectorMatrix()
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="VectorMatrix"/> class.
        /// </summary>
        /// <param name="_aValue">The _a value.</param>
        public VectorMatrix(double[,] _aValue)
        {
            this.Values = _aValue;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="VectorMatrix"/> class.
        /// </summary>
        /// <param name="_nColumns">The _n columns.</param>
        /// <param name="_nRows">The _n rows.</param>
        public VectorMatrix(int _nColumns, int _nRows)
        {
            m_aValue = new double[_nColumns, _nRows];
            m_nRows = _nRows;
            m_nColumns = _nColumns;

            m_bIsSquare = (m_nRows == m_nColumns);
        }

        /// <summary>
        /// Recalculates the values.
        /// </summary>
        /// <param name="_del">The _del.</param>
        /// <returns></returns>
        public VectorMatrix RecalculateValues(testdel _del)
        {
            for (int y = 0; y < m_nRows; y++)
            {
                for (int x = 0; x < m_nColumns; x++)
                {
                    m_aValue[x, y] = _del(m_aValue[x,y]);
                }
            }
            return this;
        }

        ///// <summary>
        ///// The function returns the determinent of a Matrix object as Fraction
        ///// </summary>
        //public static Fraction Determinant(VectorMatrix matrix)
        //{
        //    Fraction det = new Fraction(0);
        //    if (matrix.Rows != matrix.Cols)
        //        throw new MatrixException("Determinant of a non-square matrix doesn't exist");
        //    if (matrix.Rows == 1)
        //        return matrix[0, 0];
        //    for (int j = 0; j < matrix.Cols; j++)
        //        det += (matrix[0, j] * Determinent(Matrix.Minor(matrix, 0, j)) * (int)System.Math.Pow(-1, 0 + j));
        //    return det;
        //}

        /// <summary>
        /// Recalculates the values.
        /// </summary>
        /// <param name="matrix">The matrix.</param>
        /// <param name="_del">The _del.</param>
        /// <returns></returns>
        public static VectorMatrix RecalculateValues(VectorMatrix matrix, testdel _del)
        {
            for (int y = 0; y < matrix.m_nRows; y++)
            {
                for (int x = 0; x < matrix.m_nColumns; x++)
                {
                    matrix.m_aValue[x, y] = _del(matrix.m_aValue[x, y]);
                }
            }
            return matrix;
        }

        /// <summary>
        /// Gets the rows.
        /// </summary>
        /// <value>The rows.</value>
        public int Rows
        {
            get { return m_nRows; }
        }

        /// <summary>
        /// Gets the columns.
        /// </summary>
        /// <value>The columns.</value>
        public int Columns
        {
            get { return m_nColumns; }
        }

        /// <summary>
        /// Gets or sets the values.
        /// </summary>
        /// <value>The values.</value>
        public double[,] Values
        {
            get { return m_aValue; }
            set
            {
                m_aValue = value;
                m_nRows = value.GetLength(0);
                m_nColumns = value.GetLength(1);
                m_bIsSquare = (m_nRows == m_nColumns);
            }
        }

        /// <summary>
        /// Gets the <see cref="System.Double"/> with the specified _n column.
        /// </summary>
        /// <value></value>
        public double this[int _nColumn, int _nRow]
        {
            get { return m_aValue[_nColumn, _nRow]; }
        }

        /// <summary>
        /// Multiplicates the specified _d multiplicator.
        /// </summary>
        /// <param name="_dMultiplicator">The _d multiplicator.</param>
        /// <returns></returns>
        public double[,] Multiplicate(double _dMultiplicator)
        {
            this.RecalculateValues(new testdel(x => x * _dMultiplicator));
            return m_aValue;
        }

        /// <summary>
        /// Operators for the Matrix object
        /// includes -(unary), and binary opertors such as +,-,*,/
        /// </summary>
        public static VectorMatrix operator -(VectorMatrix matrix)
        { 
            return VectorMatrix.Negate(matrix); 
        }

        /// <summary>
        /// Implements the operator +.
        /// </summary>
        /// <param name="matrix1">The matrix1.</param>
        /// <param name="matrix2">The matrix2.</param>
        /// <returns>The result of the operator.</returns>
        public static VectorMatrix operator +(VectorMatrix matrix1, VectorMatrix matrix2)
        { 
            return Add(matrix1, matrix2); 
        }

        /// <summary>
        /// Implements the operator +.
        /// </summary>
        /// <param name="matrix1">The matrix1.</param>
        /// <param name="_dValue">The _d value.</param>
        /// <returns>The result of the operator.</returns>
        public static VectorMatrix operator +(VectorMatrix matrix1, double _dValue)
        {
            return matrix1.RecalculateValues(new testdel(x => x + _dValue));
        }

        //public static VectorMatrix operator -(VectorMatrix matrix1, VectorMatrix matrix2)
        //{ 
        //    return VectorMatrix.Add(matrix1, -matrix2); 
        //}

        //public static VectorMatrix operator *(VectorMatrix matrix1, VectorMatrix matrix2)
        //{ return VectorMatrix.Multiply(matrix1, matrix2); }

        /// <summary>
        /// Implements the operator *.
        /// </summary>
        /// <param name="matrix1">The matrix1.</param>
        /// <param name="iNo">The i no.</param>
        /// <returns>The result of the operator.</returns>
        public static VectorMatrix operator *(VectorMatrix matrix1, int iNo)
        { 
            return matrix1.RecalculateValues(new testdel(x => x*iNo)); 
        }

        /// <summary>
        /// Implements the operator *.
        /// </summary>
        /// <param name="matrix1">The matrix1.</param>
        /// <param name="iNo">The i no.</param>
        /// <returns>The result of the operator.</returns>
        public static VectorMatrix operator *(VectorMatrix matrix1, double iNo)
        {
            return matrix1.RecalculateValues(new testdel(x => x * iNo));
        }

        /// <summary>
        /// Implements the operator *.
        /// </summary>
        /// <param name="iNo">The i no.</param>
        /// <param name="matrix1">The matrix1.</param>
        /// <returns>The result of the operator.</returns>
        public static VectorMatrix operator *(int iNo, VectorMatrix matrix1)
        {
            return matrix1.RecalculateValues(new testdel(x => x * iNo));
        }

        /// <summary>
        /// Implements the operator *.
        /// </summary>
        /// <param name="dbl">The DBL.</param>
        /// <param name="matrix1">The matrix1.</param>
        /// <returns>The result of the operator.</returns>
        public static VectorMatrix operator *(double dbl, VectorMatrix matrix1)
        {
            return matrix1.RecalculateValues(new testdel(x => x * dbl));
        }

        //public static VectorMatrix operator *(Fraction frac, VectorMatrix matrix1)
        //{ return Matrix.Multiply(matrix1, frac); }

        //public static VectorMatrix operator /(VectorMatrix matrix1, int iNo)
        //{ return VectorMatrix.Multiply(matrix1, VectorMatrix.Inverse(new Fraction(iNo))); }

        //public static VectorMatrix operator /(VectorMatrix matrix1, double dbl)
        //{ return VectorMatrix.Multiply(matrix1, Fraction.Inverse(Fraction.ConvertToFraction(dbl))); }

        //public static VectorMatrix operator /(VectorMatrix matrix1, Fraction frac)
        //{ return Matrix.Multiply(matrix1, Fraction.Inverse(frac)); }

        /// <summary>
        /// Internal Fucntions for the above operators
        /// </summary>
        private static VectorMatrix Negate(VectorMatrix matrix)
        {
            return RecalculateValues(matrix, new testdel(x => -x));
        }

        /// <summary>
        /// Divides the specified _d divisor.
        /// </summary>
        /// <param name="_dDivisor">The _d divisor.</param>
        /// <returns></returns>
        public double[,] Divide(double _dDivisor)
        {
            this.RecalculateValues(new testdel(x => x / _dDivisor));
            return m_aValue;
        }

        /// <summary>
        /// Pows the specified _d power.
        /// </summary>
        /// <param name="_dPower">The _d power.</param>
        /// <returns></returns>
        public double[,] Pow(double _dPower)
        {
            this.RecalculateValues(new testdel(x => Math.Pow(x,_dPower) ));
            return m_aValue;
        }

        ///// <summary>
        ///// The function returns the inverse of the current matrix
        ///// </summary>
        //public VectorMatrix Inverse()
        //{
        //    return Inverse(this);
        //}

        ///// <summary>
        ///// The function returns the inverse of a given matrix
        ///// </summary>
        //public static VectorMatrix Inverse(VectorMatrix matrix)
        //{
        //    if (Determinent(matrix) == 0)
        //        throw new MatrixException("Inverse of a singular matrix is not possible");
        //    return (Adjoint(matrix) / Determinent(matrix));
        //}


        /// <summary>
        /// Adds the specified _vector matrix1.
        /// </summary>
        /// <param name="_vectorMatrix1">The _vector matrix1.</param>
        /// <param name="_vectorMatrix2">The _vector matrix2.</param>
        /// <returns></returns>
        public static VectorMatrix Add(VectorMatrix _vectorMatrix1, VectorMatrix _vectorMatrix2)
        {
            if (_vectorMatrix1.m_nRows != _vectorMatrix2.m_nRows || _vectorMatrix1.Columns != _vectorMatrix2.m_nColumns)
            {
                throw new Exception("Operation not possible");
            }
            for (int y = 0; y < _vectorMatrix1.m_nRows; y++)
            {
                for (int x = 0; x < _vectorMatrix1.m_nColumns; x++)
                {
                    _vectorMatrix1.m_aValue[x, y] += _vectorMatrix2.m_aValue[x, y];
                }
            }
            return _vectorMatrix1;
        }

        /// <summary>
        /// Adds the specified _d sum.
        /// </summary>
        /// <param name="_dSum">The _d sum.</param>
        /// <returns></returns>
        public double[,] Add(double _dSum)
        {
            this.RecalculateValues(new testdel(x => x + _dSum));
            return m_aValue;
        }

        /// <summary>
        /// Subs the specified _d sum.
        /// </summary>
        /// <param name="_dSum">The _d sum.</param>
        /// <returns></returns>
        public double[,] Sub(double _dSum)
        {
            this.RecalculateValues(new testdel(x => x + _dSum));
            return m_aValue;
        }

        /// <summary>
        /// Logs this instance.
        /// </summary>
        /// <returns></returns>
        public double[,] Log()
        {
            this.RecalculateValues(new testdel(x => Math.Log(x)));
            return m_aValue;
        }

        /// <summary>
        /// Logs the specified _d number.
        /// </summary>
        /// <param name="_dNumber">The _d number.</param>
        /// <param name="_dValue">The _d value.</param>
        /// <returns></returns>
        public double[,] Log(double _dNumber, double _dValue)
        {
            this.RecalculateValues(new testdel(x => Math.Log(x, _dValue)));         
            return m_aValue;
        }

        /// <summary>
        /// Abses this instance.
        /// </summary>
        /// <returns></returns>
        public double[,] Abs()
        {
            this.RecalculateValues(new testdel(x => Math.Abs(x)));
            return m_aValue;
        }

        /// <summary>
        /// Acoses this instance.
        /// </summary>
        /// <returns></returns>
        public double[,] Acos()
        {
            this.RecalculateValues(new testdel(x => Math.Acos(x)));
            return m_aValue;
        }

        /// <summary>
        /// Asins this instance.
        /// </summary>
        /// <returns></returns>
        public double[,] Asin()
        {
            this.RecalculateValues(new testdel(x => Math.Asin(x)));
            return m_aValue;
        }

        /// <summary>
        /// Atans this instance.
        /// </summary>
        /// <returns></returns>
        public double[,] Atan()
        {
            this.RecalculateValues(new testdel(x => Math.Atan(x)));
            return m_aValue;
        }

        /// <summary>
        /// Coses this instance.
        /// </summary>
        /// <returns></returns>
        public double[,] Cos()
        {
            this.RecalculateValues(new testdel(x => Math.Cos(x)));
            return m_aValue;
        }

        /// <summary>
        /// Coshes this instance.
        /// </summary>
        /// <returns></returns>
        public double[,] Cosh()
        {
            this.RecalculateValues(new testdel(x => Math.Cosh(x)));
            return m_aValue;
        }

        /// <summary>
        /// Exps this instance.
        /// </summary>
        /// <returns></returns>
        public double[,] Exp()
        {
            this.RecalculateValues(new testdel(x => Math.Exp(x)));
            return m_aValue;
        }

        /// <summary>
        /// Multiplicates the specified _vector matrix.
        /// </summary>
        /// <param name="_vectorMatrix">The _vector matrix.</param>
        /// <returns></returns>
        public VectorMatrix Multiplicate(VectorMatrix _vectorMatrix)
        {
            return null;
        }

        /// <summary>
        /// The function returns the transpose of a given matrix
        /// </summary>
        public VectorMatrix Transpose(VectorMatrix matrix)
        {
            VectorMatrix TransposeMatrix = new VectorMatrix(m_nColumns, m_nRows);
            for (int i = 0; i < TransposeMatrix.Rows; i++)
            {
                for (int j = 0; j < TransposeMatrix.Columns; j++)
                {
                    //TransposeMatrix[i, j] = matrix[j, i];
                }
            }
            return TransposeMatrix;
        }

        /// <summary>
        /// The function returns the transpose of the current matrix
        /// </summary>
        public VectorMatrix Transpose()
        {
            return Transpose(this);
        }

    }
}
